// this part of the mem manager takes care of paging related code
#include "mm.h"
#include "kernel.h"

// these are defined in cr1.s
extern u32 pde_phys, pde_virt;

// page directorys
pde_t* kernel_pde = 0;
pde_t* cur_pde = 0;

// page fault handler
void page_fault() {
  kprintf("  Page Fault at %p\n\0", get_CR2());;
  while(1);
}

// sets up kernel pages to point to those created by cr1.s
void init_paging() {
  u32 i;
  // setup kernel pde to point to our tables
  kernel_pde = &pde_virt;
  cur_pde = kernel_pde;
 
  // register page fault handler.
  register_isr(14, page_fault);

  // Now, enable paging!
  switch_page_directory(kernel_pde);
}

pde_t* make_user_space(u32 start, u32 end) {
  u32 i, idx;
  pte_t* table;
  pde_t* user_pde = (pde_t*)kmalloc_a(sizeof(pde_t));
  memset((u32*)user_pde, 0, sizeof(pde_t));
  
  for(i = PG_ALIGN(start); i < PG_ALIGN(end); i += 0x1000) {
    if(user_pde->tables[PDE_OFFSET(i)] == 0) {
      user_pde->tables[PDE_OFFSET(i)] = (pte_t*)((u32)kmalloc_a(sizeof(pte_t))|0x3);
      user_pde->tables_phys[PDE_OFFSET(i)] = virt_to_phys(cur_pde, user_pde->tables[PDE_OFFSET(i)]);
      memset((u8*)(user_pde->tables[PDE_OFFSET(i)]), 0, sizeof(pte_t)-3);
    }
  
    idx = get_frame();
    //get rid of access bits to access the pte correctly
    table = ((u32)user_pde->tables[PDE_OFFSET(i)]) & 0xFFFFF000;
    table->pages[PTE_OFFSET(i)] = idx | PG_PRES | PG_USER | PG_WRITE;
  }

  // make sure kernel space is mapped into user process
  for(i = 0; i < 0x3ff; ++i) {
    if(kernel_pde->tables[i] != 0) {
      user_pde->tables[i] = kernel_pde->tables[i];
      user_pde->tables_phys[i] = kernel_pde->tables_phys[i];
    }
  }
  
  return user_pde;
}  

void switch_page_directory(pde_t* dir) {
  cur_pde = dir;
  load_CR3(virt_to_phys(cur_pde, (u32)dir->tables_phys));
  enable_paging();
}

u32 virt_to_phys(pde_t* dir, u32 addr) {
  u32 result;
  // get table address by making sure to get rid of access bits
  pte_t* table = (pte_t*)PG_FRAME((u32)dir->tables[PDE_OFFSET(addr)]);
  result = PG_FRAME(table->pages[PTE_OFFSET(addr)]);
  return result + (addr & 0x00000FFF);
}

